home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-06-14 | 15.2 KB | 636 lines | [TEXT/CWIE] |
- /*------------------------------------------------------------------------------
- #
- # NewsTicker, my Hack for 1997
- #
- # OTPollEndPoint.cp - A general OT Endpoint class., designed to be derived.
- #
- # After you open the connection, calling its DoIdle routine will check for
- # any incoming data, and if found, call a derived method (broken into
- # lines, if desired). Also, when the connection is broken, it will call a
- # derived shutdown method.
- #
- # Not to be treated as a great example of OT programming, but it works.
- #
- ------------------------------------------------------------------------------*/
-
- #include "OTPollEndPoint.h"
- #include "Idler.h"
-
- typedef struct LookupNameReplyHeader
- {
- unsigned short muiAddrLen;
- unsigned short muiNameLen;
- } LookupNameReplyHeader;
-
- const OTTimeout kOTResolveTimeout = 40; // 40 seconds
-
- // Just create the class, don't do anything now
- OTPollEndPoint::OTPollEndPoint (void)
- {
- mfCancelled = false;
- mfDataWaiting = false;
- mfNeedsDisconnecting = false;
- mfLostConnection = false;
- mfDataInIsText = true;
- mulResolveTimeout = kOTResolveTimeout;
- mulResolveTimeout *= 1000L; // Convert to milliseconds
- mpEndpoint = nil;
- mfBytesReceived = 0;
- }
-
- // Destructor
- OTPollEndPoint::~OTPollEndPoint (void)
- {
- OSErr iErr = Close (nil);
- }
-
- #pragma push
- #pragma segment Main
- // Returns true if the OTPollEndPoint::Cancel() has been called since the endpoint was opened, OR
- // if the connection has been broken (by the other end disconnecting, for example)
- // In keeping with the Connection class cancelling is an immediate, synchronous operation. Cancelling
- // is expected to cause all further operations to fail immediately, thus eventually exiting out of the
- // operation.
- Boolean OTPollEndPoint::EndPointValid (void) const
- {
- return (mpEndpoint != nil && !mfCancelled);
- }
-
-
- OSErr OTPollEndPoint::DestroyEndPoint (void)
- {
- OSErr iErr = noErr;
-
- if (mpEndpoint != nil)
- {
- (void) OTSetSynchronous (mpEndpoint); // Guaranteed to succeed as per OT docs
- OTRemoveNotifier (mpEndpoint);
- iErr = OTCloseProvider(mpEndpoint);
- mpEndpoint = nil;
- mfDataWaiting = false;
- }
- return iErr;
- }
-
- void OTPollEndPoint::CheckOTError (OTResult lError)
- {
- if (lError == kOTLookErr)
- {
- if ((mpMapperRef == nil)&&(mpEndpoint != nil))
- {
- OSErr iDummy;
- OTResult lResult;
-
- switch (OTLook (mpEndpoint))
- {
- case T_ORDREL:
- lResult = OTRcvOrderlyDisconnect (mpEndpoint);
- iDummy = DestroyEndPoint ();
- break;
- case T_DISCONNECT:
- lResult = OTRcvDisconnect (mpEndpoint, nil);
- iDummy = DestroyEndPoint ();
- break;
- }
- }
- }
- }
- #pragma pop
-
-
- OSErr OTPollEndPoint::OpenEndpoint(const char *protocol)
- {
- OSStatus err;
-
- mfDataWaiting = false;
-
- mpEndpoint = OTOpenEndpoint (OTCreateConfiguration(protocol), 0, nil, &err);
- CheckOTError (err);
- if (err != noErr)
- mpEndpoint = nil;
- return err;
- }
-
- #define USE_INET_RESOLVER
-
- OSErr OTPollEndPoint::ResolveAddress (Idler& oIdler, const char* pszHost, InetHost& lHostsIPAddress)
- {
- #ifdef USE_INET_RESOLVER
- OSErr iResult;
- OSStatus lErr;
-
- mpMapperRef = nil;
-
- lErr = OTInetStringToHost ((char*) pszHost, &lHostsIPAddress);
- // If host name wasn't an IP address (www.xxx.yyy.zzz) then we must resolve it
- if (lErr != noErr)
- {
- InetHostInfo sHostInfo;
-
- miAsyncErr = noErr;
- meState = keOpeningMapper;
-
- lErr = OTAsyncOpenInternetServices (kDefaultInternetServicesPath, 0, Notifier, this);
- CheckOTError (lErr);
- if (lErr == noErr)
- {
- do {
- oIdler.YieldTime ();
- } while (meState == keOpeningMapper && !mfCancelled);
-
- iResult = miAsyncErr;
- CheckOTError (iResult);
- if (iResult == noErr)
- {
- miAsyncErr = noErr;
- meState = keResolving;
- iResult = OTInetStringToAddress ((InetSvcRef) mpMapperRef, (char*) pszHost, &sHostInfo);
- CheckOTError (iResult);
- if (iResult == noErr)
- {
- do {
- oIdler.YieldTime ();
- } while (meState == keResolving && !mfCancelled);
-
- iResult = miAsyncErr;
- if (iResult == noErr && !mfCancelled)
- lHostsIPAddress = sHostInfo.addrs [0];
- }
- if (iResult == noErr)
- iResult = OTCloseProvider (mpMapperRef);
- else
- (void) OTCloseProvider (mpMapperRef);
- mpMapperRef = nil;
- }
- }
- else
- iResult = lErr;
- }
- else
- iResult = noErr;
- return iResult;
- #else
- OSErr iErr;
- OSStatus lResult;
-
- mpMapperRef = nil;
- miAsyncErr = noErr;
- meState = keOpeningMapper;
- oIdler.YieldTime ();
- #ifdef ASYNC_MAPPER_OPEN
- lResult = OTAsyncOpenMapper (OTCreateConfiguration (kDNRName), 0, &Notifier, this);
- #else
- mpMapperRef = OTOpenMapper (OTCreateConfiguration (kDNRName), 0, &lResult);
- #endif // ASYNC_MAPPER_OPEN
- CheckOTError (lResult);
- if (lResult == kOTNoError)
- {
- #ifdef ASYNC_MAPPER_OPEN
- do {
- oIdler.YieldTime ();
- } while (meState == keOpeningMapper && !mfCancelled); // Note we aren't dealing with mpEndpoint
- iErr = miAsyncErr;
- CheckOTError (iErr);
- #else
- iErr = OTInstallNotifier (mpMapperRef, &Notifier, this);
- {
- OSErr iIgnore = OTSetAsynchronous (mpMapperRef); // Guaranteed to succeed as per OT docs
- }
- #endif // ASYNC_MAPPER_OPEN
- if (iErr == noErr && !mfCancelled)
- {
- OSErr iCloseErr;
- short iReplyBufferLen;
- char* pReplyBuffer;
-
- iReplyBufferLen = sizeof (LookupNameReplyHeader) + sizeof (InetAddress) + strlen (pszHost) + 5;
- pReplyBuffer = new char [iReplyBufferLen];
- if (pReplyBuffer != nil)
- {
- size_t iiHostLen = strlen (pszHost);
- TLookupRequest oRequest = {
- { iiHostLen, iiHostLen, (UInt8*) pszHost },
- { 0, 0, nil },
- 1, // Max # of addresses returned by resolver
- mulResolveTimeout
- };
- TLookupReply oReply = {
- { iReplyBufferLen, iReplyBufferLen, (UInt8*) pReplyBuffer },
- 1
- };
-
- miAsyncErr = noErr;
- meState = keResolving;
- oIdler.YieldTime ();
- lResult = OTLookupName (mpMapperRef, &oRequest, &oReply);
- CheckOTError (lResult);
- if (lResult == kOTNoError && !mfCancelled) // Note we aren't dealing with mpEndpoint
- {
- do {
- oIdler.YieldTime ();
- } while (meState == keResolving && !mfCancelled); // Note we aren't dealing with mpEndpoint
- iErr = miAsyncErr;
- CheckOTError (iErr);
- if (iErr == noErr && !mfCancelled) // Note we aren't dealing with mpEndpoint
- {
- InetAddress* psResolvedAddress;
-
- psResolvedAddress = (InetAddress*) (pReplyBuffer + sizeof (LookupNameReplyHeader));
- lHostsIPAddress = psResolvedAddress->fHost;
- }
- }
- else
- iErr = lResult;
- delete [] pReplyBuffer;
- }
- else
- iErr = memFullErr;
-
- if (mpMapperRef != nil)
- {
- iCloseErr = OTCloseProvider (mpMapperRef);
- mpMapperRef = nil;
- CheckOTError (iCloseErr);
- if (iErr == noErr)
- iErr = iCloseErr;
- oIdler.YieldTime ();
- }
- }
-
- if (mfCancelled)
- iErr = keOTPAborted;
- }
- else
- iErr = lResult;
- return iErr;
- #endif // USE_INET_RESOLVER
- }
-
-
- // Put call back in main segment to avoid having the segment unloaded and causing typical callback problems
- // if the segment is unloaded when the callback is called
- #pragma push
- #pragma segment Main
-
- // *** Called at deferred task time. ***
- pascal void OTPollEndPoint::Notifier(void* contextPtr, OTEventCode code, OTResult result, void* cookie)
- {
- OTPollEndPoint* thisPoint = (OTPollEndPoint*) contextPtr;
-
- thisPoint->HandleNotify (code, result, cookie);
- }
-
-
- // *** Called at deferred task time. ***
- void OTPollEndPoint::HandleNotify( OTEventCode code, OTResult result, void* cookie)
- {
- OTResult lResult;
-
- switch (code)
- {
- case T_BINDCOMPLETE:
- miAsyncErr = result;
- meState = keBindingDone;
- break;
- case T_CONNECT:
- miAsyncErr = result;
- if (result == kOTNoError)
- {
- if (mpEndpoint != nil)
- {
- lResult = OTRcvConnect (mpEndpoint, nil);
- CheckOTError (lResult);
- }
- meState = keConnectingDone;
- }
- else
- meState = keBailingOut;
- break;
- case T_DISCONNECT:
- miAsyncErr = result;
- meState = keDisconnecting;
- // the endpoint may have been shut down by close call so check for nil
- if (mpEndpoint != nil)
- {
- lResult = OTRcvDisconnect (mpEndpoint, nil);
- CheckOTError (lResult);
- DestroyEndPoint ();
- }
- meState = keDisconnectingDone;
- mfLostConnection = true;
- break;
- case T_ORDREL:
- miAsyncErr = result;
- meState = keOrderlyDisconnecting;
- // the endpoint may have been shut down by close call so check for nil
- if (mpEndpoint != nil)
- {
- OSErr iDummy;
-
- lResult = OTRcvOrderlyDisconnect (mpEndpoint);
- CheckOTError (lResult);
- if (OTGetEndpointState(mpEndpoint)!=T_IDLE)
- {
- OTSndOrderlyDisconnect(mpEndpoint);
- }
- iDummy = DestroyEndPoint ();
- }
- meState = keOrderlyDisconnectingDone;
- mfLostConnection = true;
- break;
- case T_DATA:
- mfDataWaiting = true;
- break;
- case T_DNRSTRINGTOADDRCOMPLETE:
- case T_LKUPNAMECOMPLETE:
- meState = keResolvingDone;
- miAsyncErr = result;
- break;
- case T_OPENCOMPLETE:
- meState = keOpeningMapperDone;
- miAsyncErr = result;
- if (result == kOTNoError)
- mpMapperRef = (MapperRef) cookie;
- break;
- }
- }
-
- #pragma pop
-
-
- OSErr OTPollEndPoint::Open (Idler& oIdler, const char *address, short port)
- {
- OSErr err;
-
- mfNeedsDisconnecting = false;
- mfCancelled = false;
- err = OpenEndpoint (kTCPName);
- if (err == noErr && EndPointValid ())
- {
- err = OTInstallNotifier (mpEndpoint, &Notifier, this);
- CheckOTError (err);
- // make it asynchronous
- {
- OSErr iIgnored = OTSetAsynchronous (mpEndpoint); // Guaranteed to succeed as per OT docs
- }
- if (err == noErr && EndPointValid ())
- {
- mfDestAddress.inet.fAddressType = AF_INET;
- mfDestAddress.inet.fPort = port;
- err = ResolveAddress (oIdler, address, mfDestAddress.inet.fHost);
- if (err == noErr && EndPointValid ())
- {
- InetAddress inAddress;
- TBind inBind = { { sizeof(InetAddress), sizeof(InetAddress), (UInt8*) &inAddress }, 0 };
-
- inAddress.fAddressType = AF_INET;
- inAddress.fPort = 0;
- inAddress.fHost = 0;
- miAsyncErr = noErr;
- meState = keBinding;
- err = OTBind (mpEndpoint, &inBind, nil);
- CheckOTError (err);
- if (err == kOTNoError)
- {
- // Use "do" loop so YieldTime is called no matter what
- do {
- oIdler.YieldTime ();
- } while (meState == keBinding && EndPointValid ());
- if (!EndPointValid ())
- err = keOTPEPaborted;
- else
- {
- err = miAsyncErr;
- CheckOTError (err);
- }
-
- if (err == noErr && EndPointValid ())
- {
- TCall sConnectionParms = {
- { sizeof(mfDestAddress), sizeof(mfDestAddress), (UInt8*) &mfDestAddress },
- { 0, 0, NULL },
- { 0, 0, NULL },
- };
-
- miAsyncErr = noErr;
- meState = keConnecting;
- err = OTConnect (mpEndpoint, &sConnectionParms, nil);
- if (err == kOTNoError || err == kOTNoDataErr)
- {
- // Use "do" loop so YieldTime is called no matter what
- do {
- oIdler.YieldTime ();
- } while (meState == keConnecting && EndPointValid ());
- if (!EndPointValid ())
- err = keOTPEPaborted;
- else
- {
- err = miAsyncErr;
- if (err == noErr)
- mfNeedsDisconnecting = true;
- else
- CheckOTError (err);
- }
- // OTRcvConnect() is done in HandleNotify()
- }
- }
- }
- }
- }
-
- // If the open fails we must close the endpoint that we know at this point was successfully opened
- // If endpoint is not valid (mpEndPoint is nil or endpoint has been cancelled) it's benign to call Close()
- if (err != noErr || !EndPointValid ())
- {
- OSErr iDummy = Close (&oIdler);
- }
- }
-
- return err;
- }
-
-
- OSErr OTPollEndPoint::Close (Idler* poIdler)
- {
- OSErr iResult = noErr;
-
- // Need to close the endpoint if Cancel() was called so we check for mpEndpoint != nil instead of EndPointValid ()
- if (mpEndpoint != nil)
- {
- OSErr iDummy;
-
- if (mfNeedsDisconnecting)
- {
- miAsyncErr = noErr;
- meState = keOrderlyDisconnecting;
- iResult = OTSndOrderlyDisconnect (mpEndpoint);
- CheckOTError (iResult);
- }
-
- iDummy = DestroyEndPoint ();
-
- if (poIdler != nil)
- poIdler->YieldTime ();
- }
- return iResult;
- }
-
-
- void OTPollEndPoint::Cancel (void)
- {
- mfCancelled = true;
- }
-
-
- OSErr OTPollEndPoint::SendData (Idler& oIdler, const void *pData, long lSize)
- {
- long lBytesSent = 0;
- OSStatus lResult = noErr;
-
- while (EndPointValid ())
- {
- lResult = OTSnd (mpEndpoint, (void*) (((UInt8*) pData) + lBytesSent), lSize, 0);
-
- if (lResult >= 0)
- {
- lBytesSent += lResult;
- if (lBytesSent >= lSize)
- {
- lResult = kOTNoError;
- break;
- }
- }
- else if (lResult != kOTFlowErr)
- {
- CheckOTError (lResult);
- break;
- }
- // else no bytes were sent - we'll just try sending again
- oIdler.YieldTime ();
- }
-
- if (!EndPointValid ())
- lResult = keOTPEPaborted;
-
- return (OSErr) lResult;
- }
-
- // Read and send data
-
- void OTPollEndPoint::DoIdle(void)
- {
- //Check and see if we've received a string
- GetData();
-
- if (!mfLostConnection && !EndPointValid)
- {
- mfLostConnection = true;
- }
-
- if (mfLostConnection&&(!mfDataWaiting)&&(!mfBytesReceived))
- {
- mfLostConnection = false;
- HandleShutdown();
- }
- }
-
- //
- // Read data and pass it off to HandleData, called by DoIdle()
- //
- OSErr OTPollEndPoint::GetData ( void )
- {
-
- OSStatus lResult = noErr;
- OTFlags flags;
- OTResult theResult;
- Boolean GotOne;
-
- short index;
-
- //if (!EndPointValid ())
- // return -1;
- if ((!mfDataWaiting)&&(!mfBytesReceived))
- return noErr;
-
- if (mfDataWaiting)
- {
- theResult = OTRcv (mpEndpoint, (void*) (&mfInComingData[mfBytesReceived]),
- sizeof(mfInComingData) - mfBytesReceived, &flags);
-
- if ((theResult < 0)&&(theResult != kOTNoDataErr))
- {
- CheckOTError (theResult);
- return (OSErr) theResult;
- }
- else if (theResult > 0)
- {
- mfBytesReceived += theResult;
-
- if ((flags & T_MORE) == 0)
- {
- mfDataWaiting = false;
- }
- }
- else mfDataWaiting = false;
- }
-
- if (!mfDataInIsText) //Should we not be parsing this?
- {
- this->HandleData(mfInComingData, mfBytesReceived); //have them handle it and clear it
- mfBytesReceived = 0;
- }
- else
- {
- GotOne = false;
- for (index = 0; index < mfBytesReceived; index++)
- {
- if (mfInComingData[index]==0x0A) //found end of line, line feed of cr/lf
- {
- GotOne = true;
- index++; //convert the index into the size
- this->HandleData(mfInComingData, index); //have them handle it and clear it
- BlockMove(&mfInComingData[index], &mfInComingData[0], mfBytesReceived-index);
- mfBytesReceived-=index;
- index = 0;
- }
- }
- if (!GotOne)
- {
- for (index = 0; index < mfBytesReceived; index++)
- {
- if (mfInComingData[index]==0x0D) //Does this one have just returns?
- {
- GotOne = true;
- index++; //convert the index into the size
- this->HandleData(mfInComingData, index); //have them handle it and clear it
- BlockMove(&mfInComingData[index], &mfInComingData[0], mfBytesReceived-index);
- mfBytesReceived-=index;
- index = 0;
- }
- }
- }
- if ((!GotOne)&&(mfBytesReceived>sizeof(mfInComingData)/2)) //it's just a whole lot with no line feed
- {
- this->HandleData(mfInComingData, mfBytesReceived); //have them handle it and clear it
- mfBytesReceived = 0;
- }
- }
-
- return noErr;
- }
-
- //
- // Override this to get the data!
- //
- void OTPollEndPoint::HandleData (char* /*thestring*/, short /*numchars*/)
- {
- //do nothing here, only here to be derived
- }
- //
- // If we need to tell someone we've closed, override this
- //
- void OTPollEndPoint::HandleShutdown (void)
- {
-
- }
-